version 0.2.1
|
|||||||||||||
Description | SQL query and Comments | ||||||||||||
Comments | There are no comments in MS
Access. So "/*", "--", and "#" can't be used here. But it's possible to use
the NULL byte (%00) to comment out the end of the query :
|
||||||||||||
Syntax Error Message | "[Microsoft][Driver ODBC Microsoft Access]" | ||||||||||||
Stacked Query | Not Allowed. | ||||||||||||
UNION support | The UNION operator is supported, but it needs a valid table name in the FROM clause. | ||||||||||||
Subquery | Subqueries are supported
(in the example "TOP 1" is used to
make the query returns only one row) :
|
||||||||||||
LIMIT support | LIMIT isn't implemented,
but it's possible to use "TOP N" in SELECT statement, to limit the number of
returned rows :
|
||||||||||||
Make the query returns 0 rows | This could be useful when
the script displays only the first result rows in the html response :
|
||||||||||||
String concatenation | No CONCAT() function exists. It's
possible to use the "&" or "+" operator to concat two strings. But you need to
URL encode them :
|
||||||||||||
Substring | MID() function :
|
||||||||||||
String length | LEN() function :
|
||||||||||||
Find web root directory | You can find the web root
directory trying
to select something from an inexistent database. MS Access will response
with an error message containing the full path name :
|
||||||||||||
ASCII value from a character | ASC() function :
|
||||||||||||
Character from an ASCII value | CHR() function :
|
||||||||||||
IF Statement | IIF() function can be used.
Syntax : IIF(condition, true, false) :
|
||||||||||||
Time Inference | Function such as BENCHMARK() or SLEEP() doesn't exist. But it's possible to inference data with the use of heavy queries as explained here. | ||||||||||||
Verify File Existence |
By injecting :
|
||||||||||||
Table Name bruteforcing | Here is a simple Java method
that could be used to bruteforce MS Access table names. I wrote it to better
explain the table name bruteforce process :
bruteTableName() takes as parameter (0) an object called "Request" (rappresenting the bugged request). The method tries to inject through sendInjection() (2) this query :
Where table[i] is an element of a table name list (1). You can find a small wordlist at the end of this paper : it can be used for this purpose. At position (2), sendInjection() returns the html response after the sql code injection. If resp contains the columnErrorMessage string (3) you have found an existent table in the database. columnErrorMessage is a message that informs you that you used a different number of columns with UNION SELECT. This is why you don't need of the columns number : you can just check it to inference the table's existence. If the injectable query selects only one attribute/column, the code determinates the table name guessing with the second condition on accessError string (it's just the syntax error message mentioned above) : infact it will be displayed only if the table doesn't exist. |
||||||||||||
Field Name bruteforcing | You need a valid table name and column
number :
In this case you have to loop on an attribute names list (in the same way I propose in the code above). An attribute is guessed if the syntax error message doesn't appear in the html response. Here "j" is just an index of an ipotetical attributes list. |
||||||||||||
Login bypass |
User : ' OR 1=1%00
(or " OR 1=1%00) Password : (blank) |
||||||||||||
Attributes Enumeration |
NOTE : This method was tested with JBoss
(using a bugged .jsp script) + MS Access, I don't know if
this works with other configurations.
Usually if a SQL Injection exists, when you type a quote (') in a URL parameter you obtain an error message such as :
Now you'll obtain a new error message contains another attribute name. Enumeration follows by injecting :
until you'll enumerate all the parameters. |
||||||||||||
System OS Interaction |
|||||||||||||
By default it's impossible to access to these functions |
|||||||||||||
Security Notes | It's possible to block the use of
critical functions (such as SHELL(), etc ...) by setting this register key :
Its default values is 2, so by default it's impossible to use these functions. What I propose below are some examples tested with that register key setted to 0. |
||||||||||||
Get Current Directory | Here you need of columns
number and a valid table name :
|
||||||||||||
Execute OS Commands | SHELL() function can be
used to run OS command :
|
||||||||||||
MS Access System Tables |
|||||||||||||
By default it's impossible to access to these system tables |
|||||||||||||
MSysAccessXML |
Table fields name :
|
||||||||||||
MSysACEs |
Table fields name :
|
||||||||||||
MSysObjects |
Here you can find database table name :
This query can be used to obtain database tables name :
|
||||||||||||
MS Access Blind SQL Injection (these steps can be used to bruteforce table content) | |||||||||||||
Step #1 : Bruteforce Table Name | You have to
bruteforce the table name. You can use the wordlist listed below. Inject query :
After injection you have to check the html response page. If table exists you should have the same html page layout (because "AND 1" has no effect on the query). |
||||||||||||
Step #2 : Bruteforce Field Name |
You have to
find table fields name. Inject query :
As above, you should check the html page layout to inferece the field name existence . |
||||||||||||
Step #3 : Bruteforce Table rows number | You have to
find the number of table rows. This value will be used as "TAB_LEN" variable
in the following queries/descriptions :
Where "X" is a number between 0 and an arbitrary value. As above you will find the correct number by checking html page layout. |
||||||||||||
Step #4 : Bruteforce item length |
You can bruteforce the value length of a generic "ATTRIB" field at row number 1 with this query :
You can bruteforce the value length of a generic "ATTRIB" field from row 2 to TAB_LEN with this query (here N is a number between 2 and TAB_LEN, the value bruteforced before) :
"KKK" is a value between 0 and an arbitrary value, while ATTRIB<>'valueXXX' is used because we have to select a specific line to bruteforce. The unique way I found to do this is to select the desidered row with "TOP N",and then insert in the WHERE clause all the attribute values bruteforced before. I have to say that "ATTRIB" must be the table key-field. Here is an example :
You can bruteforce fields value length for row 1 in this way :
While you can bruteforce fields length value of second row in this way (assuming A1 as table key-field) :
The same for row number 3 :
Obviusly, before bruteforcing field value length (at row between 2 and TAB_LEN) you have to bruteforce the previous row (key) field value (you have to put it in the WHERE clause). |
||||||||||||
Step #5 : Bruteforce Table Content | Supposing that the attacker
already knows the table and fields name, he will inject this query :
Where "N" is the row to bruteforce, "XXX" is the x-th byte of "ATTRIBxxx" to bruteforce, "ATT_key" is the table key-field and "YYY" is a number between 0 and 255 (it represents the ASCII value for a char). Here we have to use the same method mentioned before to correctly bruteforce a specific row attribute content. |
||||||||||||
Tables/Fields Bruteforcing (Wordlist) | |||||||||||||
Table/Field Wordlist | Here is a very short table/field
names wordlist that can be used during bruteforcing :
|
||||||||||||
Links and References | |||||||||||||
|
|||||||||||||